import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
from matplotlib.animation import FuncAnimation
import matplotlib.cm as cm
from scipy import signal
import pandas as pd
import seaborn as sns
import pywt
from scipy.signal import welch
from scipy.interpolate import griddata


# stft得到的时频图平滑处理
def smooth_spectrum(Zxx, window_len=5):
    smoothed_Zxx = np.copy(Zxx)
    for i in range(Zxx.shape[0]):
        smoothed_Zxx[i, :] = np.convolve(np.abs(Zxx[i, :]), np.ones(window_len) / window_len, mode='same')
    return smoothed_Zxx


def save_or_show(is_save,save_path):
    if is_save:
        plt.savefig(save_path)
    else:
        plt.show()


def title_set(is_title,is_xtitle,is_ytitle,title,x_title,y_title,title_fontsize,subtitle_fontsize,font_family,font_weight):
    if is_title:
        plt.title(title, fontsize=title_fontsize, family=font_family, weight=font_weight)
    if is_xtitle:
        plt.xlabel(x_title, fontsize=subtitle_fontsize, family=font_family, weight=font_weight)
    if is_ytitle:
        plt.ylabel(y_title, fontsize=subtitle_fontsize, family=font_family, weight=font_weight)


def plot_origin_signal(data, **kwargs):
    """
    :param data: 包含神经信号的数据,为numpy数组，数据大小为(n,1)或者(n,)，n表示数据长度
    :param kwargs:
    figure_size: 画布大小，类型(x,y)，x为画布的宽，y为画布的高，默认为（12，6），可设置
    data_color: 数据线条颜色，为string类型，默认为None，可设置
    linewidth: 数据线条粗细，为float类型，默认为1，可设置
    font_family: 文字字体，为string类型，默认为'Times New Roman'，可设置
    title_fontsize: 大标题文字大小，为float类型，默认为24，可设置
    subtitle_fontsizr: 坐标轴标题文字大小，为float类型，默认为18，可设置
    font_weight: 字体加粗，为string类型，默认为'bold'，可设置
    is_title: 是否设置大标题，为bool类型，默认为True，可设置
    is_xtitle: 是否设置横轴标题，为bool类型，默认为True，可设置
    is_ytitle: 是否设置纵轴标题，为bool类型，默认为True，可设置
    x_lim: 横轴坐标范围，类型[start,end],默认为None，可设置
    y_lim: 纵轴坐标范围，类型[start,end],默认为None，可设置
    title: 大标题名称，为string类型，默认为None，可设置
    x_title: 横轴标题名称，为string类型，默认为None，可设置
    y_title: 纵轴标题名称，为string类型，默认为None，可设置
    is_save: 是否保存图片，为bool类型，默认为False，可设置
    save_path: 保存图片路劲，为string类型，默认为'original_signal.png'，可设置
    :return:
    """
    figure_size = kwargs.get('figure_size', (12, 6))
    data_color = kwargs.get('data_color', None)
    linewidth = kwargs.get('linewidth', 1)
    font_family = kwargs.get('font_family', 'Times New Roman')
    title_fontsize = kwargs.get('title_fontsize', 24)
    subtitle_fontsize = kwargs.get('subtitle_fontsize', 18)
    font_weight = kwargs.get('font_weight', 'bold')
    is_title = kwargs.get('is_title', True)
    is_xtitle = kwargs.get('is_xtitle', True)
    is_ytitle = kwargs.get('is_ytitle', True)
    x_lim = kwargs.get('x_lim', None)
    y_lim = kwargs.get('y_lim', None)
    title = kwargs.get('title', None)
    x_title = kwargs.get('x_title', None)
    y_title = kwargs.get('y_title', None)
    is_save = kwargs.get('is_save', False)
    save_path = kwargs.get('save_path', 'original_signal.png')

    plt.figure(figsize=figure_size)
    plt.plot(data, color=data_color, linewidth=linewidth)
    title_set(is_title, is_xtitle, is_ytitle, title, x_title, y_title, title_fontsize, subtitle_fontsize, font_family,
              font_weight)
    plt.xlim(x_lim)
    plt.ylim(y_lim)
    save_or_show(is_save,save_path)


def plot_box(data, **kwargs):
    """
    :param data: 需要箱线图表示的数据，为numpy数组形式（data,num），data为数据，num为箱线图个数
    :param kwargs:
    figure_size: 画布大小，类型(x,y)，x为画布的宽，y为画布的高，默认为（12，6），可设置
    font_family: 文字字体，为string类型，默认为'Times New Roman'，可设置
    title_fontsize: 大标题文字大小，为float类型，默认为24，可设置
    subtitle_fontsizr: 坐标轴标题文字大小，为float类型，默认为18，可设置
    font_weight: 字体加粗，为string类型，默认为'bold'，可设置
    is_title: 是否设置大标题，为bool类型，默认为True，可设置
    is_xtitle: 是否设置横轴标题，为bool类型，默认为True，可设置
    is_ytitle: 是否设置纵轴标题，为bool类型，默认为True，可设置
    title: 大标题名称，为string类型，默认为None，可设置
    x_title: 横轴标题名称，为string类型，默认为None，可设置
    y_title: 纵轴标题名称，为string类型，默认为None，可设置
    is_save: 是否保存图片，为bool类型，默认为False，可设置
    save_path: 保存图片路劲，为string类型，默认为'box.png'，可设置
    notch: 是否设置缺口形状箱线图，为bool类型，默认为False，可设置
    box_color: 箱线图箱子的颜色，为string类型，默认为'black'，可设置
    box_linewidth: 箱线图箱子线的粗细，为float类型，默认为1，可设置
    box_linestyle: 箱线图箱子线的类型，为string类型，默认为'-'，可设置
    marker: 异常值表示类型，为string类型，默认为'o'，可设置
    markersize: 异常值表示大小，为float类型，默认为'5'，可设置
    markeredgecolor: 异常值表示颜色，为string类型，默认为'black'，可设置
    labels: 每个箱线图对应的标签，为n个string类型，n要对应输入的data数据中的num，默认为None，可设置
    :return:
    """
    figure_size = kwargs.get('figure_size', (12, 6))
    font_family = kwargs.get('font_family', 'Times New Roman')
    title_fontsize = kwargs.get('title_fontsize', 24)
    subtitle_fontsize = kwargs.get('subtitle_fontsize', 18)
    font_weight = kwargs.get('font_weight', 'bold')
    is_title = kwargs.get('is_title', True)
    is_xtitle = kwargs.get('is_xtitle', True)
    is_ytitle = kwargs.get('is_ytitle', True)
    title = kwargs.get('title', None)
    x_title = kwargs.get('x_title', None)
    y_title = kwargs.get('y_title', None)
    is_save = kwargs.get('is_save', False)
    save_path = kwargs.get('save_path', 'box.png')

    notch = kwargs.get('notch', False)

    box_color = kwargs.get('box_color', 'black')
    box_linewidth = kwargs.get('box_linewidth', 1)
    box_linestyle = kwargs.get('box_linestyle', '-')

    marker = kwargs.get('marker', 'o')
    markersize = kwargs.get('marker', 5)
    markeredgecolor = kwargs.get('markeredgecolor', 'black')

    labels = kwargs.get('labels', None)

    boxprops = dict(color=box_color, linewidth=box_linewidth, linestyle=box_linestyle)
    flierprops = dict(marker=marker, markersize=markersize, markeredgecolor=markeredgecolor)

    plt.figure(figsize=figure_size)
    plt.boxplot(data, notch=notch, labels=labels, boxprops=boxprops, flierprops=flierprops)
    title_set(is_title, is_xtitle, is_ytitle, title, x_title, y_title, title_fontsize, subtitle_fontsize, font_family,
              font_weight)
    save_or_show(is_save,save_path)


def plot_violin(data, **kwargs):
    """
    :param data: 需要小提琴图表示的数据，为dictionary字典形式，里面第一个键值表示数据标签，第二个键值表示数据内容。例：
    data = {
    'Frequency': ['LMP', '0.5 - 4 Hz', '4 - 8 Hz', '8 - 12 Hz', '12 - 25 Hz', '25 - 50 Hz', '50 - 100 Hz',
                  '100 - 200 Hz', '200 - 400 Hz', 'Latent dynamics'],
    'Predictive Accuracy': [np.random.rand(100) for _ in range(10)]
    }
    :param kwargs:
    figure_size: 画布大小，类型(x,y)，x为画布的宽，y为画布的高，默认为（12，6），可设置
    palette: 小提琴图调色板颜色，为string类型，默认为'viridis'，可设置
    inner: 小提琴内部数据点表示，为string类型，默认为'box'，可设置，有'box','quartile','point','stick',None
    font_family: 文字字体，为string类型，默认为'Times New Roman'，可设置
    title_fontsize: 大标题文字大小，为float类型，默认为24，可设置
    subtitle_fontsizr: 坐标轴标题文字大小，为float类型，默认为18，可设置
    font_weight: 字体加粗，为string类型，默认为'bold'，可设置
    is_title: 是否设置大标题，为bool类型，默认为True，可设置
    is_xtitle: 是否设置横轴标题，为bool类型，默认为True，可设置
    is_ytitle: 是否设置纵轴标题，为bool类型，默认为True，可设置
    title: 大标题名称，为string类型，默认为None，可设置
    x_title: 横轴标题名称，为string类型，默认为None，可设置
    y_title: 纵轴标题名称，为string类型，默认为None，可设置
    is_save: 是否保存图片，为bool类型，默认为False，可设置
    save_path: 保存图片路劲，为string类型，默认为'original_signal.png'，可设置
    :return:
    """
    figure_size = kwargs.get('figure_size', (12, 6))
    palette = kwargs.get('palette', 'viridis')
    inner = kwargs.get('inner', 'box')
    font_family = kwargs.get('font_family', 'Times New Roman')

    title_fontsize = kwargs.get('title_fontsize', 24)
    subtitle_fontsize = kwargs.get('subtitle_fontsize', 18)
    font_weight = kwargs.get('font_weight', 'bold')

    is_title = kwargs.get('is_title', True)
    is_xtitle = kwargs.get('is_xtitle', True)
    is_ytitle = kwargs.get('is_ytitle', True)
    title = kwargs.get('title', None)
    x_title = kwargs.get('x_title', 'x')
    y_title = kwargs.get('y_title', 'y')
    is_save = kwargs.get('is_save', False)
    save_path = kwargs.get('save_path', 'violin.png')

    df = pd.DataFrame({
        x_title: np.repeat(data['Frequency'], 100),
        y_title: np.concatenate(data['Predictive Accuracy'])
    })

    plt.figure(figsize=figure_size)
    sns.violinplot(data=df, x=x_title, y=y_title, palette=palette, inner=inner)
    title_set(is_title, is_xtitle, is_ytitle, title, x_title, y_title, title_fontsize, subtitle_fontsize, font_family,
              font_weight)
    save_or_show(is_save,save_path)


def plot_correlation(data, **kwargs):
    """
    :param data: 需要表示通道之间互相关性的数据，为numpy数组形式（num_channel,time），num_channel为数据通道数，time为数据时间长
    :param kwargs:
    figure_size: 画布大小，类型(x,y)，x为画布的宽，y为画布的高，默认为（14，12），可设置
    cmap: 热图颜色，为string类型，默认为'coolwarm'，可设置
    annot: 是否显示每个格子的数值，为bool类型,默认为False，可设置
    font_family: 文字字体，为string类型，默认为'Times New Roman'，可设置
    title_fontsize: 大标题文字大小，为float类型，默认为24，可设置
    subtitle_fontsizr: 坐标轴标题文字大小，为float类型，默认为18，可设置
    font_weight: 字体加粗，为string类型，默认为'bold'，可设置
    tick_fontsize: 横纵轴刻度字体大小，为float类型，默认为10，可设置
    is_title: 是否设置大标题，为bool类型，默认为True，可设置
    is_xtitle: 是否设置横轴标题，为bool类型，默认为True，可设置
    is_ytitle: 是否设置纵轴标题，为bool类型，默认为True，可设置
    title: 大标题名称，为string类型，默认为None，可设置
    x_title: 横轴标题名称，为string类型，默认为None，可设置
    y_title: 纵轴标题名称，为string类型，默认为None，可设置
    tick_interval: 横纵轴刻度间隔，为int类型，默认为10，可设置
    xtick_rotation: 横轴刻度字体旋转度数，为float类型，默认为45,可设置
    ytick_rotation 纵轴刻度字体旋转度数，为float类型，默认为0，可设置
    is_save: 是否保存图片，为bool类型，默认为False，可设置
    save_path: 保存图片路劲，为string类型，默认为'correlation.png'，可设置
    :return:
    """
    figure_size = kwargs.get('figure_size', (14, 12))
    cmap = kwargs.get('cmap', 'coolwarm')
    annot = kwargs.get('annot', False)
    font_family = kwargs.get('font_family', 'Times New Roman')
    title_fontsize = kwargs.get('title_fontsize', 24)
    subtitle_fontsize = kwargs.get('subtitle_fontsize', 18)
    font_weight = kwargs.get('font_weight', 'bold')
    tick_fontsize = kwargs.get('tick_fontsize', 10)
    is_title = kwargs.get('is_title', True)
    is_xtitle = kwargs.get('is_xtitle', True)
    is_ytitle = kwargs.get('is_ytitle', True)
    title = kwargs.get('title', None)
    x_title = kwargs.get('x_title', None)
    y_title = kwargs.get('y_title', None)
    tick_interval = kwargs.get('tick_interval', 10)
    xtick_rotation = kwargs.get('xtick_rotation', 45)
    ytick_rotation = kwargs.get('ytick_rotation', 0)
    is_save = kwargs.get('is_save', False)
    save_path = kwargs.get('save_path', 'correlation.png')

    correlation_matrix = np.corrcoef(data)

    # 设置绘图风格
    sns.set(style='white', font_scale=1)

    # 创建一个大的绘图区域
    plt.figure(figsize=figure_size)

    # 绘制热力图
    ax = sns.heatmap(correlation_matrix,
                     cmap=cmap,
                     vmin=-1, vmax=1,
                     annot=annot,  # 设置为True以显示每个格子的数值
                     fmt=".2f",
                     linewidths=0.5,
                     cbar_kws={"shrink": 1},  # 调整颜色条的尺寸
                     square=True)

    # 添加标题和标签
    plt.title('Channel Correlation Matrix Heatmap', fontsize=24, pad=20)
    plt.xlabel('Channel', fontsize=18)
    plt.ylabel('Channel', fontsize=18)
    title_set(is_title=is_title, is_xtitle=is_xtitle, is_ytitle=is_ytitle, title=title, x_title=x_title,
              y_title=y_title, title_fontsize=title_fontsize, subtitle_fontsize=subtitle_fontsize,
              font_family=font_family, font_weight=font_weight)

    ax.set_xticks(np.arange(0, correlation_matrix.shape[0], tick_interval))
    ax.set_yticks(np.arange(0, correlation_matrix.shape[0], tick_interval))

    # 调整轴标签的大小和旋转角度
    ax.set_xticklabels(np.arange(0, correlation_matrix.shape[0], tick_interval), rotation=xtick_rotation,
                       fontsize=tick_fontsize)
    ax.set_yticklabels(np.arange(0, correlation_matrix.shape[0], tick_interval), rotation=ytick_rotation,
                       fontsize=tick_fontsize)

    # 删除图形周围的顶部和右侧边框线
    sns.despine()

    # 保存还是展示图
    save_or_show(is_save=is_save, save_path=save_path)


def plot_video_stft_spectrogram(data, window_size=2.0, interval=2.0, video_type='series', **kwargs):
    """
    :param data: 输入的神经信号数据，为numpy数组，大小为(n,),n表示数据长度
    :param window_size: 以固定窗口展示时的窗口大小，为float类型，单位为秒，默认为2.0，可设置
    :param interval: 连续展示时的间隔大小，为float类型，单位为秒，默认为2.0，可设置
    :param video_type: 展示类型，为string类型，默认为'fixed_win'，可设置，有（'fixed_win','series'）
    :param kwargs:
    figure_size: 画布大小，类型(x,y)，x为画布的宽，y为画布的高，默认为（12，6），可设置
    font_family: 文字字体，为string类型，默认为'Times New Roman'，可设置
    title_fontsize: 大标题文字大小，为float类型，默认为24，可设置
    subtitle_fontsizr: 坐标轴标题文字大小，为float类型，默认为18，可设置
    font_weight: 字体加粗，为string类型，默认为'bold'，可设置
    is_title: 是否设置大标题，为bool类型，默认为True，可设置
    is_xtitle: 是否设置横轴标题，为bool类型，默认为True，可设置
    is_ytitle: 是否设置纵轴标题，为bool类型，默认为True，可设置
    x_lim: 横轴坐标范围，类型[start,end],默认为None，可设置
    y_lim: 纵轴坐标范围，类型[start,end],默认为None，可设置
    title: 大标题名称，为string类型，默认为None，可设置
    x_title: 横轴标题名称，为string类型，默认为None，可设置
    y_title: 纵轴标题名称，为string类型，默认为None，可设置
    is_save: 是否保存图片，为bool类型，默认为False，可设置
    save_path: 保存图片路劲，为string类型，默认为'original_signal.png'，可设置
    fs: 神经信号频率，为int类型，默认为1000，根据信号不同进行设置
    win_func: 短时傅里叶变换需要的窗函数，为string类型，默认为'hamming'，常用的窗函数有hamming，hann，boxcar，triang，可设置
    nperseg: 窗函数的长度，为int类型，默认为256，可设置
    noverlap: 窗函数重叠书，应小于nperseg，默认为128，可设置
    is_smooth: 是否对时频图进行平滑处理，为bool类型，默认为True，可设置
    smooth_window_size: 平滑窗口大小，为int类型，默认为10，可设置
    :return:
    """
    figure_size = kwargs.get('figure_size', (12, 6))
    font_family = kwargs.get('font_family', 'Times New Roman')
    title_fontsize = kwargs.get('title_fontsize', 24)
    subtitle_fontsize = kwargs.get('subtitle_fontsize', 18)
    font_weight = kwargs.get('font_weight', 'bold')
    is_title = kwargs.get('is_title', True)
    is_xtitle = kwargs.get('is_xtitle', True)
    is_ytitle = kwargs.get('is_ytitle', True)
    x_lim = kwargs.get('x_lim', None)
    y_lim = kwargs.get('y_lim', None)
    title = kwargs.get('title', None)
    x_title = kwargs.get('x_title', None)
    y_title = kwargs.get('y_title', None)
    is_save = kwargs.get('is_save', False)
    save_path = kwargs.get('save_path', 'spectrogram.png')

    fs = kwargs.get('fs',1000)
    win_func = kwargs.get('win_func','hamming')
    nperseg = kwargs.get('nperseg',256)
    noverlap = kwargs.get('noverlap',128)

    is_smooth = kwargs.get('is_smooth',True)
    smooth_window_size = kwargs.get('smooth_window_size',10)

    f, t, Zxx = signal.stft(data, fs=fs, window=win_func, nperseg=nperseg, noverlap=noverlap)

    if is_smooth:
        Zxx = smooth_spectrum(Zxx,smooth_window_size)

    window_size = int(np.ceil(window_size / t[1]))
    interval = int(np.ceil(interval / t[1]))

    if is_save:
        plt.figure(figsize=figure_size)
        plt.pcolormesh(t, f, np.abs(Zxx), cmap='jet')
        title_set(is_title, is_xtitle, is_ytitle, title, x_title, y_title, title_fontsize, subtitle_fontsize,
                  font_family, font_weight)
        plt.xlim(x_lim)
        plt.ylim(y_lim)
        plt.colorbar()
        plt.savefig(save_path)
    else:
        colorbar = True
        fig, ax = plt.subplots(figsize=figure_size)
        if video_type == 'fixed_win':
            for i in range(0, Zxx.shape[1], window_size):
                ax.clear()
                im = ax.pcolormesh(t[i:i + window_size], f, np.abs(Zxx[:, i:i + window_size]), cmap='jet')
                if is_title:
                    ax.set_title(title, fontsize=title_fontsize, family=font_family, weight=font_weight)
                if is_xtitle:
                    ax.set_xlabel(x_title, fontsize=subtitle_fontsize, family=font_family, weight=font_weight)
                if is_ytitle:
                    ax.set_ylabel(y_title, fontsize=subtitle_fontsize, family=font_family, weight=font_weight)
                ax.set_xlim(x_lim)
                ax.set_ylim(y_lim)
                if colorbar:
                    plt.colorbar(im, ax=ax)
                    colorbar = False
                plt.draw()
                plt.pause(1)

        elif video_type == 'series':
            for i in range(0, Zxx.shape[1], interval):
                ax.clear()
                im = ax.pcolormesh(t[:i + 1], f, np.abs(Zxx[:, :i + 1]), cmap='jet')
                if is_title:
                    ax.set_title(title, fontsize=title_fontsize, family=font_family, weight=font_weight)
                if is_xtitle:
                    ax.set_xlabel(x_title, fontsize=subtitle_fontsize, family=font_family, weight=font_weight)
                if is_ytitle:
                    ax.set_ylabel(y_title, fontsize=subtitle_fontsize, family=font_family, weight=font_weight)
                ax.set_xlim(x_lim)
                ax.set_ylim(y_lim)
                if colorbar:
                    plt.colorbar(im, ax=ax)
                    colorbar = False
                plt.draw()
                plt.pause(1)


def plot_spectrogram(data, **kwargs):
    """
    :param data: 需要频谱图表示的数据，为numpy数组形式，数据大小为(n,)，n表示数据长度
    :param kwargs:
    figure_size: 画布大小，类型(x,y)，x为画布的宽，y为画布的高，默认为（12，6），可设置
    font_family: 文字字体，为string类型，默认为'Times New Roman'，可设置
    title_fontsize: 大标题文字大小，为float类型，默认为24，可设置
    subtitle_fontsizr: 坐标轴标题文字大小，为float类型，默认为18，可设置
    font_weight: 字体加粗，为string类型，默认为'bold'，可设置
    is_title: 是否设置大标题，为bool类型，默认为True，可设置
    is_xtitle: 是否设置横轴标题，为bool类型，默认为True，可设置
    is_ytitle: 是否设置纵轴标题，为bool类型，默认为True，可设置
    title: 大标题名称，为string类型，默认为'Frequency Spectrum'，可设置
    x_title: 横轴标题名称，为string类型，默认为'Frequency (Hz)'，可设置
    y_title: 纵轴标题名称，为string类型，默认为'Amplitude'，可设置
    is_save: 是否保存图片，为bool类型，默认为False，可设置
    save_path: 保存图片路劲，为string类型，默认为'fre_spectrum.png'，可设置
    fs: 计算频谱时的数据的采样率，为int类型，默认为1000，可设置
    :return:
    """
    figure_size = kwargs.get('figure_size', (12, 6))
    font_family = kwargs.get('font_family', 'Times New Roman')

    title_fontsize = kwargs.get('title_fontsize', 24)
    subtitle_fontsize = kwargs.get('subtitle_fontsize', 18)
    font_weight = kwargs.get('font_weight', 'bold')

    is_title = kwargs.get('is_title', True)
    is_xtitle = kwargs.get('is_xtitle', True)
    is_ytitle = kwargs.get('is_ytitle', True)
    title = kwargs.get('title', 'Frequency Spectrum')
    x_title = kwargs.get('x_title', 'Frequency (Hz)')
    y_title = kwargs.get('y_title', 'Amplitude')
    is_save = kwargs.get('is_save', False)
    save_path = kwargs.get('save_path', 'fre_spectrum.png')

    fs = kwargs.get('fs', 1000)

    N = data.shape[0]
    spectrum = np.fft.fft(data)
    frequencies = np.fft.fftfreq(N, 1 / fs)

    # 只取前半部分频谱（因为 FFT 的结果是对称的）
    half_N = N // 2
    spectrum = spectrum[:half_N]
    print(spectrum)
    frequencies = frequencies[:half_N]
    print(frequencies)

    plt.figure(figsize=figure_size)
    plt.plot(frequencies, np.log10(np.abs(spectrum)))
    # 添加标题和标签
    title_set(is_title, is_xtitle, is_ytitle, title, x_title, y_title, title_fontsize, subtitle_fontsize, font_family,
              font_weight)
    plt.xlim([-100, frequencies[-1]])
    plt.grid()
    save_or_show(is_save,save_path)


def plot_wavelet_spectrogram(data, **kwargs):
    """
    :param data: 需要小波变换时频图表示的数据，为numpy数组形式，数据大小为(n,)，n表示数据长度
    :param kwargs:
    figure_size: 画布大小，类型(x,y)，x为画布的宽，y为画布的高，默认为（12，6），可设置
    cmap: 时频图颜色，为string类型，默认为'jet'，可设置
    font_family: 文字字体，为string类型，默认为'Times New Roman'，可设置
    title_fontsize: 大标题文字大小，为float类型，默认为24，可设置
    subtitle_fontsizr: 坐标轴标题文字大小，为float类型，默认为18，可设置
    font_weight: 字体加粗，为string类型，默认为'bold'，可设置
    is_title: 是否设置大标题，为bool类型，默认为True，可设置
    is_xtitle: 是否设置横轴标题，为bool类型，默认为True，可设置
    is_ytitle: 是否设置纵轴标题，为bool类型，默认为True，可设置
    title: 大标题名称，为string类型，默认为'Spectrogram'，可设置
    x_title: 横轴标题名称，为string类型，默认为'Time [s]'，可设置
    y_title: 纵轴标题名称，为string类型，默认为'Frequency [Hz]'，可设置
    is_save: 是否保存图片，为bool类型，默认为False，可设置
    save_path: 保存图片路劲，为string类型，默认为'psd.png'，可设置
    time_length: 时频图想要展示的时间长度，为int类型，默认为60，可设置
    fs: 计算小波变换时频图时的数据的采样率，为int类型，默认为1000，可设置
    wavelet: 小波变换类型，为string类型，默认为'morl'，可设置
    min_freq: 时频图想要观察的频段的起始频率，为float类型，默认为0.5，可设置
    max_freq: 时频图想要观察的频段的截止频率，为float类型，默认为200，可设置
    resolution: 频率分辨率，越大分辨率就越高，计算时间越长，为int类型，默认为200
    :return:
    """
    figure_size = kwargs.get('figure_size', (12, 6))
    cmap = kwargs.get('cmap','jet')
    font_family = kwargs.get('font_family', 'Times New Roman')

    title_fontsize = kwargs.get('title_fontsize', 24)
    subtitle_fontsize = kwargs.get('subtitle_fontsize', 18)
    font_weight = kwargs.get('font_weight', 'bold')

    is_title = kwargs.get('is_title', True)
    is_xtitle = kwargs.get('is_xtitle', True)
    is_ytitle = kwargs.get('is_ytitle', True)
    title = kwargs.get('title', 'Spectrogram')
    x_title = kwargs.get('x_title', 'Time [s]')
    y_title = kwargs.get('y_title', 'Frequency [Hz]')
    is_save = kwargs.get('is_save', False)
    save_path = kwargs.get('save_path', 'psd.png')

    time_length = kwargs.get('time_length',60)
    fs = kwargs.get('fs', 1000)
    wavelet = kwargs.get('wavelet','morl')
    min_freq = kwargs.get('min_freq',0.5)
    max_freq = kwargs.get('max_freq',200)
    resolution = kwargs.get('resolution',200)

    # 小波变换时频图
    t = np.arange(0, time_length, 1 / fs)
    data = data[:time_length * fs]
    print(t.shape)
    print(data.shape)

    # 计算尺度范围
    center_freq = pywt.central_frequency(wavelet)
    sampling_period = 1 / fs
    min_scale = center_freq / (max_freq * sampling_period)
    max_scale = center_freq / (min_freq * sampling_period)
    scales = np.arange(min_scale, max_scale, (max_scale - min_scale) / resolution)

    print('123')
    # 进行连续小波变换
    coefficients, frequencies = pywt.cwt(data, scales, wavelet, sampling_period=sampling_period)
    print(frequencies)
    print('ok')

    # 绘制时频图
    plt.figure(figsize=figure_size)
    plt.pcolormesh(t, frequencies, np.log10(np.abs(coefficients)), shading='auto', cmap=cmap)

    title_set(is_title, is_xtitle, is_ytitle, title, x_title, y_title, title_fontsize, subtitle_fontsize, font_family,
              font_weight)

    plt.ylim([min_freq, max_freq])  # 限制频率范围
    plt.colorbar()

    save_or_show(is_save,save_path)


def plot_power_ele(data,**kwargs):
    """
    :param data: 需要进行通道能量表示的数据，为numpy数组形式，数据(column,row)，每个元素表示一个通道的数据
    :param kwargs:
    figure_size: 画布大小，类型(x,y)，x为画布的宽，y为画布的高，默认为（12，6），可设置
    is_transparent: 是否以背景透明的形式展示和保存，为bool类型，默认为True，可设置
    cmap: 通道能量颜色表示，为string类型，默认为'inferno'，可设置
    font_family: 文字字体，为string类型，默认为'Times New Roman'，可设置
    title_fontsize: 大标题文字大小，为float类型，默认为24，可设置
    subtitle_fontsizr: 坐标轴标题文字大小，为float类型，默认为18，可设置
    font_weight: 字体加粗，为string类型，默认为'bold'，可设置
    is_title: 是否设置大标题，为bool类型，默认为True，可设置
    is_xtitle: 是否设置横轴标题，为bool类型，默认为True，可设置
    is_ytitle: 是否设置纵轴标题，为bool类型，默认为True，可设置
    is_cbtitle: 是否设置能量棒标题，为bool类型，默认为True，可设置
    title: 大标题名称，为string类型，默认为'Energy Distribution Across Channels'，可设置
    x_title: 横轴标题名称，为string类型，默认为'Electrode X-axis'，可设置
    y_title: 纵轴标题名称，为string类型，默认为'Electrode Y-axis'，可设置
    cb_title: 能量棒标题名称，为string类型，默认为'Energy'，可设置
    is_save: 是否保存图片，为bool类型，默认为False，可设置
    save_path: 保存图片路劲，为string类型，默认为'power_ele.gif'，可设置
    time: 通道能量图一帧东环展示的多少时间长度内的能量，为float类型，默认为1，可设置
    fs: 数据的采样率，为int类型，默认为1000，可设置
    fps: 动画播放的帧数，为int类型，默认为10，可设置
    :return:
    """
    figure_size = kwargs.get('figure_size', (8, 8))
    is_transparent = kwargs.get('is_transparent', True)
    cmap = kwargs.get('cmap','inferno')
    font_family = kwargs.get('font_family', 'Times New Roman')

    title_fontsize = kwargs.get('title_fontsize', 24)
    subtitle_fontsize = kwargs.get('subtitle_fontsize', 18)
    font_weight = kwargs.get('font_weight', 'bold')

    is_title = kwargs.get('is_title', True)
    is_xtitle = kwargs.get('is_xtitle', True)
    is_ytitle = kwargs.get('is_ytitle', True)
    is_cbtitle = kwargs.get('is_cbtitle',True)
    title = kwargs.get('title', 'Energy Distribution Across Channels')
    x_title = kwargs.get('x_title', 'Electrode X-axis')
    y_title = kwargs.get('y_title', 'Electrode Y-axis')
    cb_title = kwargs.get('cb_title', 'Energy')
    is_save = kwargs.get('is_save', False)
    save_path = kwargs.get('save_path', 'power_ele.gif')

    time = kwargs.get('time',1)
    fs = kwargs.get('fs',1000)
    fps = kwargs.get('fps',10)


    time_len = int(time * fs)
    num_bin = int(np.ceil(data[0, 0].shape[0] / time_len))
    print(num_bin)

    # 所有通道能量计算
    energy_list = []
    for i in range(num_bin):
        energy = np.zeros((data.shape[0], data.shape[1]))
        for j in range(data.shape[0]):
            for k in range(data.shape[1]):
                energy[j, k] = np.sum(data[j, k][(i * time_len):((i + 1) * time_len)] ** 2)
        energy_list.append(energy)
    stacked_arr = np.stack(energy_list)
    max_value = np.max(stacked_arr)

    # 创建一个图形和子图
    fig, ax = plt.subplots(figsize=figure_size)
    if is_transparent:
        fig.patch.set_alpha(0)  # 设置图形背景透明
        ax.set_facecolor('none')  # 设置子图背景透明

    if is_title:
        ax.set_title(title, fontsize=title_fontsize, fontfamily=font_family,weight=font_weight)
    if is_xtitle:
        ax.set_xlabel(x_title, fontsize=subtitle_fontsize, fontfamily=font_family,weight=font_weight)
    if is_ytitle:
        ax.set_ylabel(y_title, fontsize=subtitle_fontsize, fontfamily=font_family,weight=font_weight)

    # 初始化第一个时间窗口的圆圈图
    sc = ax.scatter(
        np.tile(np.arange(energy_list[0].shape[1]), energy_list[0].shape[0]),
        np.repeat(np.arange(energy_list[0].shape[0]), energy_list[0].shape[1]),
        c=energy_list[0].flatten(), cmap=cmap, vmin=0, vmax=max_value, s=500
    )
    colorbar = plt.colorbar(sc, ax=ax)
    if is_cbtitle:
        colorbar.set_label(cb_title, fontsize=subtitle_fontsize, fontfamily=font_family)

    # 更新每一帧的图像
    def update_frame(i):
        sc.set_array(energy_list[i].flatten())
        return [sc]

    # 创建动画
    ani = animation.FuncAnimation(fig, update_frame, frames=len(energy_list), blit=True, repeat=False)

    # 保存动画为 GIF 文件或者展示
    if is_save:
        ani.save(save_path, writer='pillow', fps=fps, savefig_kwargs={'transparent': is_transparent})  # fps=8 表示每秒8帧
    else:
        plt.show()


def plot_channel_fre(data,**kwargs):
    """
    :param data: 需要进行所有通道频率热图表示的数据，为numpy数组形式，数据(num_channels,num_samples)，num_channels表示通道数，num_samples表示时间长
    :param kwargs:
    figure_size: 画布大小，类型(x,y)，x为画布的宽，y为画布的高，默认为（12，8），可设置
    cmap: 通道能量颜色表示，为string类型，默认为'viridis'，可设置
    font_family: 文字字体，为string类型，默认为'Times New Roman'，可设置
    title_fontsize: 大标题文字大小，为float类型，默认为24，可设置
    subtitle_fontsizr: 坐标轴标题文字大小，为float类型，默认为18，可设置
    font_weight: 字体加粗，为string类型，默认为'bold'，可设置
    is_title: 是否设置大标题，为bool类型，默认为True，可设置
    is_xtitle: 是否设置横轴标题，为bool类型，默认为True，可设置
    is_ytitle: 是否设置纵轴标题，为bool类型，默认为True，可设置
    title: 大标题名称，为string类型，默认为'Spectral Frequency'，可设置
    x_title: 横轴标题名称，为string类型，默认为'Frequency [Hz]'，可设置
    y_title: 纵轴标题名称，为string类型，默认为'Channel'，可设置
    is_save: 是否保存图片，为bool类型，默认为False，可设置
    save_path: 保存图片路劲，为string类型，默认为'ch_power.png'，可设置
    fs: 数据的采样率，为int类型，默认为1000，可设置
    :return:
    """
    figure_size = kwargs.get('figure_size', (12, 8))
    cmap = kwargs.get('cmap', 'viridis')
    font_family = kwargs.get('font_family', 'Times New Roman')

    title_fontsize = kwargs.get('title_fontsize', 24)
    subtitle_fontsize = kwargs.get('subtitle_fontsize', 18)
    font_weight = kwargs.get('font_weight', 'bold')

    is_title = kwargs.get('is_title', True)
    is_xtitle = kwargs.get('is_xtitle', True)
    is_ytitle = kwargs.get('is_ytitle', True)
    title = kwargs.get('title', 'Spectral Frequency')
    x_title = kwargs.get('x_title', 'Frequency [Hz]')
    y_title = kwargs.get('y_title', 'Channel')
    is_save = kwargs.get('is_save', False)
    save_path = kwargs.get('save_path', 'ch_power.png')

    fs = kwargs.get('fs', 1000)

    num_channels, num_samples = data.shape

    half_N = num_samples // 2
    energy = np.zeros((num_channels, half_N), dtype=complex)

    for i in range(num_channels):
        each = data[i, :]
        spectrum = np.fft.fft(each)[:half_N]
        energy[i, :] = spectrum

    frequencies = np.fft.fftfreq(num_samples, 1 / fs)[:half_N]

    plt.figure(figsize=figure_size)
    plt.imshow(np.log2(np.abs(energy)), aspect='auto', extent=[frequencies.min(), frequencies.max(), num_channels, 1],
               cmap=cmap)
    plt.colorbar()
    title_set(is_title, is_xtitle, is_ytitle, title, x_title, y_title, title_fontsize, subtitle_fontsize, font_family,
              font_weight)
    save_or_show(is_save,save_path)


def plot_psd_topography(data, **kwargs):
    """
    :param data: 需要能量谱密度折线图表示的数据，为numpy数组形式，数据为(channel,time_length),channel为通道数，time_length为时间长,或者(column,row)，每个元素表示一个通道的数据。
    :param kwargs:
    figure_size: 画布大小，类型(x,y)，x为画布的宽，y为画布的高，默认为（12，6），可设置
    cmap: 地形图颜色，类型为string，默认为'inferno'，可设置
    isoline_color: 等势线颜色，类型为string，默认为'black'，可设置
    isoline_width: 等势线粗细，类型为float，默认为0.5，可设置
    ele_color: 电极颜色，类型为string，默认为'red'，可设置
    font_family: 文字字体，为string类型，默认为'Times New Roman'，可设置
    title_fontsize: 大标题文字大小，为float类型，默认为24，可设置
    subtitle_fontsizr: 坐标轴标题文字大小，为float类型，默认为18，可设置
    font_weight: 字体加粗，为string类型，默认为'bold'，可设置
    is_title: 是否设置大标题，为bool类型，默认为True，可设置
    is_xtitle: 是否设置横轴标题，为bool类型，默认为True，可设置
    is_ytitle: 是否设置纵轴标题，为bool类型，默认为True，可设置
    is_cbtitle: 是否设置能量棒标题，为bool类型，默认为True，可设置
    title: 大标题名称，为string类型，默认为'Power Spectral Density Topography'，可设置
    x_title: 横轴标题名称，为string类型，默认为'Electrode Column'，可设置
    y_title: 纵轴标题名称，为string类型，默认为'Electrode Row'，可设置
    cb_title: 能量棒标题，为string类型，默认为'Power Spectral Density'，可设置
    is_save: 是否保存图片，为bool类型，默认为False，可设置
    save_path: 保存图片路劲，为string类型，默认为'psd_topography.png'，可设置
    x_spacing: 行电极间距，为float类型，默认为7，可设置
    y_spacing: 列电极间距，为float类型，默认为2，可设置
    ele_radium: 电极半径，为float类型，默认为1，可设置
    fs: 计算PSD时的数据的采样率，为int类型，默认为1000，可设置
    nperseg: 计算PSD时的每段长度，为int类型，默认为1000，可设置
    freq_band: 频率范围，为tuple类型，默认为(120,200)，可设置
    :return:
    """
    figure_size = kwargs.get('figure_size', (10, 8))
    cmap = kwargs.get('cmap','inferno')
    isoline_color = kwargs.get('isoline_color','black')
    isoline_width = kwargs.get('isoline_width',0.5)
    ele_color = kwargs.get('ele_color', 'red')
    font_family = kwargs.get('font_family', 'Times New Roman')

    title_fontsize = kwargs.get('title_fontsize', 24)
    subtitle_fontsize = kwargs.get('subtitle_fontsize', 18)
    font_weight = kwargs.get('font_weight', 'bold')

    is_title = kwargs.get('is_title', True)
    is_xtitle = kwargs.get('is_xtitle', True)
    is_ytitle = kwargs.get('is_ytitle', True)
    is_cbtitle = kwargs.get('is_cbtitle', True)
    title = kwargs.get('title', 'Power Spectral Density Topography')
    x_title = kwargs.get('x_title', 'Electrode Column')
    y_title = kwargs.get('y_title', 'Electrode Row')
    cb_title = kwargs.get('cb_title', 'Power Spectral Density')
    is_save = kwargs.get('is_save', False)
    save_path = kwargs.get('save_path', 'psd_topography.png')

    x_spacing = kwargs.get('x_spacing',7)
    y_spacing = kwargs.get('y_spacing',2)
    ele_radium = kwargs.get('ele_radium',1)

    fs = kwargs.get('fs', 1000)
    nperseg = kwargs.get('nperseg', 1000)
    freq_band = kwargs.get('freq_band',(120,200))


    psds = []
    frequencies = None

    # 计算每个通道的PSD
    if len(data[0, 0].shape) == 0:
        for i in range(data.shape[0]):
            freqs, psd = welch(data[i], fs=fs, nperseg=nperseg)
            psds.append(psd)
            if frequencies is None:
                frequencies = freqs
        psds = np.array(psds)
        print(psds.shape)
    else:
        for i in range(data.shape[0]):
            for k in range(data.shape[1]):
                freqs, psd = welch(data[i, k], fs=fs, nperseg=nperseg)
                psds.append(psd)
                if frequencies is None:
                    frequencies = freqs
        psds = np.array(psds)
        print(psds.shape)

    freq_mask = (frequencies >= freq_band[0]) & (frequencies <= freq_band[1])
    psd_band = psds[:, freq_mask].mean(axis=1)

    if psd_band.shape[0] == 128:
        psd_grid = psd_band.reshape(16, 8)
        x = np.arange(8) * x_spacing
        y = np.arange(16) * y_spacing
    elif psd_band.shape[0] == 96:
        psd_grid = psd_band.reshape(12, 8)
        x = np.arange(8) * x_spacing
        y = np.arange(12) * y_spacing
    else:
        print('Wrong electrode numbers, please rectify the code by yourself!')

    xv, yv = np.meshgrid(x, y)
    zv = psd_grid

    xi = np.linspace(x.min() - x_spacing, x.max() + x_spacing, 50)
    yi = np.linspace(y.min() - y_spacing, y.max() + y_spacing, 100)
    xi, yi = np.meshgrid(xi, yi)
    zi = griddata((xv.flatten(), yv.flatten()), zv.flatten(), (xi, yi), method='cubic')
    zi[np.isnan(zi)] = np.nanmin(zi)

    plt.figure(figsize=figure_size)
    contourf = plt.contourf(xi, yi, zi, levels=20, cmap=cmap)
    contour = plt.contour(xi, yi, zi, levels=20, colors=isoline_color, linewidths=isoline_width)
    colorbar = plt.colorbar(contourf)

    if is_cbtitle:
        colorbar.set_label(label=cb_title, fontsize=subtitle_fontsize, fontfamily=font_family)
    title_set(is_title, is_xtitle, is_ytitle, title, x_title, y_title, title_fontsize, subtitle_fontsize, font_family,
              font_weight)
    plt.scatter(xv, yv, color=ele_color, s=(ele_radium * 8) ** 2)
    plt.xlim(xi.min(), xi.max())
    plt.ylim(yi.min(), yi.max())
    plt.gca().set_aspect('equal', adjustable='box')

    save_or_show(is_save,save_path)


def plot_video_psd_topography(data, **kwargs):
    """
    :param data: 需要能量谱密度折线图表示的数据，为numpy数组形式，数据为(column,row)，每个元素表示一个通道的数据。
    :param kwargs:
    figure_size: 画布大小，类型(x,y)，x为画布的宽，y为画布的高，默认为（12，6），可设置
    cmap: 地形图颜色，类型为string，默认为'inferno'，可设置
    isoline_color: 等势线颜色，类型为string，默认为'black'，可设置
    isoline_width: 等势线粗细，类型为float，默认为0.5，可设置
    ele_color: 电极颜色，类型为string，默认为'red'，可设置
    font_family: 文字字体，为string类型，默认为'Times New Roman'，可设置
    title_fontsize: 大标题文字大小，为float类型，默认为24，可设置
    subtitle_fontsizr: 坐标轴标题文字大小，为float类型，默认为18，可设置
    font_weight: 字体加粗，为string类型，默认为'bold'，可设置
    is_title: 是否设置大标题，为bool类型，默认为True，可设置
    is_xtitle: 是否设置横轴标题，为bool类型，默认为True，可设置
    is_ytitle: 是否设置纵轴标题，为bool类型，默认为True，可设置
    is_cbtitle: 是否设置能量棒标题，为bool类型，默认为True，可设置
    title: 大标题名称，为string类型，默认为'Power Spectral Density Topography'，可设置
    x_title: 横轴标题名称，为string类型，默认为'Electrode Column'，可设置
    y_title: 纵轴标题名称，为string类型，默认为'Electrode Row'，可设置
    cb_title: 能量棒标题，为string类型，默认为'Power Spectral Density'，可设置
    is_save: 是否保存图片，为bool类型，默认为False，可设置
    save_path: 保存图片路劲，为string类型，默认为'psd_topography.gif'，可设置
    x_spacing: 行电极间距，为float类型，默认为7，可设置
    y_spacing: 列电极间距，为float类型，默认为2，可设置
    ele_radium: 电极半径，为float类型，默认为1，可设置
    time: 能量谱密度图一帧动画展示的多少时间长度内的能量，为float类型，默认为1，可设置
    fs: 计算PSD时的数据的采样率，为int类型，默认为1000，可设置
    nperseg: 计算PSD时的每段长度，为int类型，默认为1000，可设置
    freq_band: 频率范围，为tuple类型，默认为(120,200)，可设置
    fps: 动画播放的帧数，为int类型，默认为10，可设置
    :return:
    """
    figure_size = kwargs.get('figure_size', (10, 8))
    cmap = kwargs.get('cmap','inferno')
    isoline_color = kwargs.get('isoline_color','black')
    isoline_width = kwargs.get('isoline_width',0.5)
    ele_color = kwargs.get('ele_color', 'red')
    font_family = kwargs.get('font_family', 'Times New Roman')

    title_fontsize = kwargs.get('title_fontsize', 24)
    subtitle_fontsize = kwargs.get('subtitle_fontsize', 18)
    font_weight = kwargs.get('font_weight', 'bold')

    is_title = kwargs.get('is_title', True)
    is_xtitle = kwargs.get('is_xtitle', True)
    is_ytitle = kwargs.get('is_ytitle', True)
    is_cbtitle = kwargs.get('is_cbtitle', True)
    title = kwargs.get('title', 'Power Spectral Density Topography')
    x_title = kwargs.get('x_title', 'Electrode Column')
    y_title = kwargs.get('y_title', 'Electrode Row')
    cb_title = kwargs.get('cb_title', 'Power Spectral Density')
    is_save = kwargs.get('is_save', False)
    save_path = kwargs.get('save_path', 'psd_topography.gif')

    x_spacing = kwargs.get('x_spacing',7)
    y_spacing = kwargs.get('y_spacing',2)
    ele_radium = kwargs.get('ele_radium',1)

    time =kwargs.get('time', 1)
    fs = kwargs.get('fs', 1000)
    nperseg = kwargs.get('nperseg', 1000)
    freq_band = kwargs.get('freq_band',(120,200))
    fps = kwargs.get('fps',10)

    time_len = int(time * fs)
    num_bin = int((np.ceil(data[0, 0].shape[0] / time_len))-1)

    psds_list = []
    x = None
    y = None
    xv = None
    yv = None
    xi = None
    yi = None

    frequencies = None
    for t in range(num_bin):
        psds = []
        # 计算每个通道的PSD
        for i in range(data.shape[0]):
            for k in range(data.shape[1]):
                freqs, psd = welch(data[i, k][(t * time_len):((t + 1) * time_len)], fs=fs, nperseg=nperseg)
                psds.append(psd)
                if frequencies is None:
                    frequencies = freqs
        psds = np.array(psds)
        print(psds.shape)


        freq_mask = (frequencies >= freq_band[0]) & (frequencies <= freq_band[1])
        psd_band = psds[:, freq_mask].mean(axis=1)

        if psd_band.shape[0] == 128:
            psd_grid = psd_band.reshape(16, 8)
            if x is None:
                x = np.arange(8) * x_spacing
                y = np.arange(16) * y_spacing
        elif psd_band.shape[0] == 96:
            psd_grid = psd_band.reshape(12, 8)
            if x is None:
                x = np.arange(8) * x_spacing
                y = np.arange(12) * y_spacing
        else:
            print('Wrong electrode numbers, please rectify the codes in the function by yourself!')

        if xv is None:
            xv, yv = np.meshgrid(x, y)
            print(1564)
            print(xv.shape)
            print(xv)
            print(yv)
        zv = psd_grid

        if xi is None:
            xi = np.linspace(x.min() - x_spacing, x.max() + x_spacing, 50)
            yi = np.linspace(y.min() - y_spacing, y.max() + y_spacing, 100)
            xi, yi = np.meshgrid(xi, yi)
        zi = griddata((xv.flatten(), yv.flatten()), zv.flatten(), (xi, yi), method='cubic')
        zi[np.isnan(zi)] = np.nanmin(zi)

        psds_list.append(zi)

    fig,ax = plt.subplots(figsize=figure_size)
    contourf = ax.contourf(xi, yi, psds_list[0], levels=20, cmap=cmap)
    contour = ax.contour(xi, yi, psds_list[0], levels=20, colors=isoline_color, linewidths=isoline_width)
    colorbar = plt.colorbar(contourf)

    if is_cbtitle:
        colorbar.set_label(label=cb_title, fontsize=subtitle_fontsize, fontfamily=font_family)

    if is_title:
        ax.set_title(title, fontsize=title_fontsize, fontfamily=font_family, fontweight=font_weight)
    if is_xtitle:
        ax.set_xlabel(x_title, fontsize=subtitle_fontsize, fontfamily=font_family, fontweight=font_weight)
    if is_ytitle:
        ax.set_ylabel(y_title, fontsize=subtitle_fontsize, fontfamily=font_family, fontweight=font_weight)

    scatter = ax.scatter(xv, yv, color=ele_color, s=(ele_radium * 8) ** 2)
    ax.set_xlim(xi.min(), xi.max())
    ax.set_ylim(yi.min(), yi.max())
    ax.set_aspect('equal', adjustable='box')

    def update(frame):
        # 清除所有旧的绘图对象
        for collection in ax.collections:
            collection.remove()
        contourf = ax.contourf(xi, yi, psds_list[frame], levels=20, cmap=cmap)
        contour = ax.contour(xi, yi, psds_list[frame], levels=20, colors=isoline_color, linewidths=isoline_width)
        scatter = ax.scatter(xv, yv, color=ele_color, s=(ele_radium * 8) ** 2)
        return [contourf] + [contour] + [scatter]

    anim = FuncAnimation(fig, update, frames=num_bin, interval=1000 / fps, blit=False)

    if is_save:
        anim.save(save_path, writer='pillow', fps=fps)
        print(f"GIF 动图已保存到 {save_path}")
    else:
        plt.show()


def plot_psd_line(data, **kwargs):
    """
    :param data: 需要能量谱密度折线图表示的数据，为numpy数组形式，数据为(channel,time_length),channel为通道数，time_length为时间长,或者(column,row)，每个元素表示一个通道的数据。
    :param kwargs:
    figure_size: 画布大小，类型(x,y)，x为画布的宽，y为画布的高，默认为（12，6），可设置
    font_family: 文字字体，为string类型，默认为'Times New Roman'，可设置
    title_fontsize: 大标题文字大小，为float类型，默认为24，可设置
    subtitle_fontsizr: 坐标轴标题文字大小，为float类型，默认为18，可设置
    font_weight: 字体加粗，为string类型，默认为'bold'，可设置
    is_title: 是否设置大标题，为bool类型，默认为True，可设置
    is_xtitle: 是否设置横轴标题，为bool类型，默认为True，可设置
    is_ytitle: 是否设置纵轴标题，为bool类型，默认为True，可设置
    title: 大标题名称，为string类型，默认为'128-Channel Power Spectral Density'，可设置
    x_title: 横轴标题名称，为string类型，默认为'Frequency'，可设置
    y_title: 纵轴标题名称，为string类型，默认为'Power Spectral Density'，可设置
    is_save: 是否保存图片，为bool类型，默认为False，可设置
    save_path: 保存图片路劲，为string类型，默认为'psd.png'，可设置
    fs: 计算PSD时的数据的采样率，为int类型，默认为1000，可设置
    nperseg: 计算PSD时的每段长度，为int类型，默认为1000，可设置
    :return:
    """
    figure_size = kwargs.get('figure_size', (12, 6))
    font_family = kwargs.get('font_family', 'Times New Roman')

    title_fontsize = kwargs.get('title_fontsize', 24)
    subtitle_fontsize = kwargs.get('subtitle_fontsize', 18)
    font_weight = kwargs.get('font_weight', 'bold')

    is_title = kwargs.get('is_title', True)
    is_xtitle = kwargs.get('is_xtitle', True)
    is_ytitle = kwargs.get('is_ytitle', True)
    title = kwargs.get('title','128-Channel Power Spectral Density')
    x_title = kwargs.get('x_title','Frequency (Hz)')
    y_title = kwargs.get('y_title','Power Spectral Density')
    is_save = kwargs.get('is_save', False)
    save_path = kwargs.get('save_path', 'psd.png')

    fs = kwargs.get('fs', 1000)
    nperseg = kwargs.get('nperseg', 1000)

    psds = []
    frequencies = None

    # 计算每个通道的PSD
    if len(data[0,0].shape) == 0:
        total_ch = data.shape[0]
        for i in range(data.shape[0]):
            freqs, psd = welch(data[i], fs=fs, nperseg=nperseg)
            psds.append(psd)
            if frequencies is None:
                frequencies = freqs
        psds = np.array(psds)
        print(psds.shape)
    else:
        total_ch = data.shape[0]*data.shape[1]
        for i in range(data.shape[0]):
            for k in range(data.shape[1]):
                freqs, psd = welch(data[i, k], fs=fs, nperseg=nperseg)
                psds.append(psd)
                if frequencies is None:
                    frequencies = freqs
        psds = np.array(psds)
        print(psds.shape)


    # 生成一个颜色映射
    colors = cm.viridis(np.linspace(0, 1, num=total_ch))

    # 创建一个新的图形
    plt.figure(figsize=figure_size)

    # 绘制每个通道的数据
    for i in range(total_ch):
        plt.plot(np.log10(psds[i]), color=colors[i])

    # 添加标题和标签
    title_set(is_title, is_xtitle, is_ytitle, title, x_title, y_title, title_fontsize, subtitle_fontsize, font_family,
              font_weight)
    # 保存还是显示图形
    save_or_show(is_save,save_path)


if __name__ == '__main__':
   a = None
   print(a)
